package nets

import (
	"net"
	"net/http"
	"net/url"
	"os/exec"
	"runtime"
	"time"
)

// PublicIpAddress 公网ip信息
type PublicIpAddress struct {
	Status      string  `json:"status"`      // 状态
	Country     string  `json:"country"`     // 国家
	CountryCode string  `json:"countryCode"` // 国家代码
	Region      string  `json:"region"`      // 地区
	RegionName  string  `json:"regionName"`  // 地区名称
	City        string  `json:"city"`        // 城市
	Lat         float64 `json:"lat"`         // 纬度
	Lon         float64 `json:"lon"`         // 经度
	Isp         string  `json:"isp"`         // 运营商
	Org         string  `json:"org"`         // 机构
	As          string  `json:"as"`          // AS
	Ip          string  `json:"query"`       // ip
	Timezone    string  `json:"timezone"`    // 时区
	Zip         string  `json:"zip"`         // 邮编
}

// GetInternalIp 获取内网ip
func GetInternalIp() string {
	adders, err := net.InterfaceAddrs()
	if err != nil {
		panic(err.Error())
	}
	for _, addr := range adders {
		var ip net.IP
		switch v := addr.(type) {
		case *net.IPNet:
			ip = v.IP
		case *net.IPAddr:
			ip = v.IP
		}
		if ip != nil && (ip.IsLinkLocalUnicast() ||
			ip.IsLoopback()) {
			continue
		}
		if ipv4 := ip.To4(); ipv4 != nil {
			return ipv4.String()
		}
	}
	return ""
}

// GetPublicIpAddress 获取公网ip
// @return *PublicIpAddress
func GetPublicIpAddress() (*PublicIpAddress, error) {
	webReq := NewWebHttpReq()
	resp, err := webReq.SetUrl("http://ip-api.com/json").
		SetMethod(http.MethodGet).
		SetHeader("Content-Type", "application/json").
		SetHeader("Accept", "application/json").
		Send()
	if err != nil {
		return nil, err
	}
	var result PublicIpAddress
	err = webReq.ParseResponseToJsonObj(resp, &result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

// GetAllIpAddressList 获取所有ip
func GetAllIpAddressList() []string {
	var ips []string
	adders, err := net.InterfaceAddrs()
	if err != nil {
		return ips
	}
	for _, addr := range adders {
		if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
			if ipNet.IP.To4() != nil {
				ips = append(ips, ipNet.IP.String())
			}
		}
	}
	return ips
}

// GetAllMacAddressList 获取所有mac地址
// @return []string
func GetAllMacAddressList() []string {
	var macs []string
	nets, err := net.Interfaces()
	if err != nil {
		return macs
	}
	for _, n := range nets {
		macAddr := n.HardwareAddr.String()
		if len(macAddr) == 0 {
			continue
		}
		macs = append(macs, macAddr)
	}
	return macs
}

// IsPublicIP 判断是否为公网ip
func IsPublicIP(ip net.IP) bool {
	if ip.IsLoopback() ||
		ip.IsLinkLocalMulticast() ||
		ip.IsLinkLocalUnicast() {
		return false
	}
	if ip4 := ip.To4(); ip4 != nil {
		switch {
		case ip4[0] == 10:
			return false
		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
			return false
		case ip4[0] == 192 && ip4[1] == 168:
			return false
		default:
			return true
		}
	}
	return false
}

// IsInternalIp 判断是否为内网ip
// @param ip net.IP
func IsInternalIp(ip net.IP) bool {
	if ip.IsLoopback() {
		return true
	}
	if ip4 := ip.To4(); ip4 != nil {
		switch {
		case ip4[0] == 10:
			return true
		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
			return true
		case ip4[0] == 192 && ip4[1] == 168:
			return true
		default:
			return false
		}
	}
	return false
}

// EncodeUrl 编码url
// @param urlStr string
func EncodeUrl(urlStr string) (string, error) {
	urlTmp, err := url.Parse(urlStr)
	if err != nil {
		return "", err
	}
	urlTmp.RawQuery = urlTmp.Query().Encode()
	return urlTmp.String(), nil
}

// IsPingConnected 判断是否可以ping通
func IsPingConnected(host string) bool {
	cmd := exec.Command("ping", host, "-c", "4", "-W", "6")

	if runtime.GOOS == "windows" {
		cmd = exec.Command("ping", host, "-n", "4", "-w", "6")
	}

	err := cmd.Run()
	if err != nil {
		return false
	}
	return true
}

// IsTelnetConnected 判断是否可以telnet通
func IsTelnetConnected(host string, port string) bool {
	adder := host + ":" + port
	conn, err := net.DialTimeout("tcp", adder, 5*time.Second)

	if err != nil {
		return false
	}

	defer func(conn net.Conn) {
		_ = conn.Close()
	}(conn)

	return true
}
